package zookeeper;

import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.thrift.transport.TSocket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * TSocket供应商
 * 
 * 采用对象池的方式，实现了TSocket链接池
 * 每个ThriftConnectionProvider对象包含一个TSocket连接池
 * 
 * 使用的时候，需要与每个服务器（ip:port）分别建立一个ThriftConnectionProvider对象。
 * 并且，由于是链接池方式，每当使用完ThriftConnectionProvider对象提供的TSocket后，
 * 务必调用returnCon来归还TSocket。
 * 
 * @author Administrator
 *
 */

public class ThriftConnectionProvider implements ConnectionProvider {
    public static final Logger logger = LoggerFactory.getLogger(ThriftConnectionProvider.class);
    /**
	 * 服务的IP地址
	 * @uml.property  name="serviceIP"
	 */
    private String serviceIP;
    /**
	 * 服务的端口
	 * @uml.property  name="servicePort"
	 */
    private int servicePort;
    /**
	 * 连接超时配置
	 * @uml.property  name="conTimeOut"
	 */
    private int conTimeOut;

    /**
	 * @uml.property  name="maxActive"
	 */
    private int maxActive = 10;      //可以从缓存池中分配对象的最大数量
    /**
	 * @uml.property  name="maxIdle"
	 */
    private int maxIdle = maxActive; //缓存池中最大空闲对象数量
    /**
	 * @uml.property  name="minIdle"
	 */
    private int minIdle = 1;         //缓存池中最小空闲对象数量
    /**
	 * @uml.property  name="maxWait"
	 */
    private long maxWait = -1;       //阻塞的最大数量
    /**
	 * 从缓存池中分配对象，是否执行PoolableObjectFactory.validateObject方法
	 * @uml.property  name="testOnBorrow"
	 */
    private boolean testOnBorrow = false;
    /**
	 * @uml.property  name="testOnReturn"
	 */
    private boolean testOnReturn = false;
    /**
	 * @uml.property  name="testWhileIdle"
	 */
    private boolean testWhileIdle = true;
    /**
	 * 对象缓存池
	 * @uml.property  name="objectPool"
	 * @uml.associationEnd  multiplicity="(0 -1)" elementType="org.apache.thrift.transport.TSocket"
	 */
    private ObjectPool objectPool = null;

    private void afterPropertiesSet() {
        ThriftPoolableObjectFactory fac = new ThriftPoolableObjectFactory(
                serviceIP, servicePort, conTimeOut);
        // 对象池
        GenericObjectPool pool = new GenericObjectPool(fac);
        pool.setMaxActive(maxActive);
        pool.setMaxIdle(maxIdle);
        pool.setMinIdle(minIdle);
        pool.setMaxWait(maxWait);
        pool.setTestOnBorrow(testOnBorrow);
        pool.setTestOnReturn(testOnReturn);
        pool.setTestWhileIdle(testWhileIdle);
        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_GROW);

        //noinspection EmptyCatchBlock
        try {
            if (objectPool != null) objectPool.close();
        } catch (Throwable t) {
        }
        objectPool = pool;
    }

    @Override
    public void close() {
        try {
            objectPool.close();
        } catch (Exception e) {
            throw new RuntimeException("erorr destroy()", e);
        }
    }

    @Override
    public TSocket getConnection() {
        try {
            return (TSocket) objectPool.borrowObject();
        } catch (Exception e) {
            throw new RuntimeException("error getConnection()", e);
        }
    }

    @Override
    public void returnCon(TSocket socket, boolean hasError) {
        try {
            if (hasError)
                objectPool.invalidateObject(socket);
            else
                objectPool.returnObject(socket);
        } catch (Exception e) {
            throw new RuntimeException("error returnCon()", e);
        }
    }

    /**
	 * @return
	 * @uml.property  name="serviceIP"
	 */
    public String getServiceIP() {
        return serviceIP;
    }

    /**
	 * @param serviceIP
	 * @uml.property  name="serviceIP"
	 */
    public void setServiceIP(String serviceIP) {
        this.serviceIP = serviceIP;
        afterPropertiesSet();
    }

    /**
	 * @return
	 * @uml.property  name="servicePort"
	 */
    public int getServicePort() {
        return servicePort;
    }

    /**
	 * @param servicePort
	 * @uml.property  name="servicePort"
	 */
    public void setServicePort(int servicePort) {
        this.servicePort = servicePort;
        afterPropertiesSet();
    }

    /**
	 * @return
	 * @uml.property  name="conTimeOut"
	 */
    public int getConTimeOut() {
        return conTimeOut;
    }

    /**
	 * @param conTimeOut
	 * @uml.property  name="conTimeOut"
	 */
    public void setConTimeOut(int conTimeOut) {
        this.conTimeOut = conTimeOut;
        afterPropertiesSet();
    }

    /**
	 * @return
	 * @uml.property  name="maxActive"
	 */
    public int getMaxActive() {
        return maxActive;
    }

    /**
	 * @param maxActive
	 * @uml.property  name="maxActive"
	 */
    public void setMaxActive(int maxActive) {
        this.maxActive = maxActive;
        afterPropertiesSet();
    }

    /**
	 * @return
	 * @uml.property  name="maxIdle"
	 */
    public int getMaxIdle() {
        return maxIdle;
    }

    /**
	 * @param maxIdle
	 * @uml.property  name="maxIdle"
	 */
    public void setMaxIdle(int maxIdle) {
        this.maxIdle = maxIdle;
        afterPropertiesSet();
    }

    /**
	 * @return
	 * @uml.property  name="minIdle"
	 */
    public int getMinIdle() {
        return minIdle;
    }

    /**
	 * @param minIdle
	 * @uml.property  name="minIdle"
	 */
    public void setMinIdle(int minIdle) {
        this.minIdle = minIdle;
        afterPropertiesSet();
    }

    /**
	 * @return
	 * @uml.property  name="maxWait"
	 */
    public long getMaxWait() {
        return maxWait;
    }

    /**
	 * @param maxWait
	 * @uml.property  name="maxWait"
	 */
    public void setMaxWait(long maxWait) {
        this.maxWait = maxWait;
        afterPropertiesSet();
    }

    /**
	 * @return
	 * @uml.property  name="testOnBorrow"
	 */
    public boolean isTestOnBorrow() {
        return testOnBorrow;
    }

    /**
	 * @param testOnBorrow
	 * @uml.property  name="testOnBorrow"
	 */
    public void setTestOnBorrow(boolean testOnBorrow) {
        this.testOnBorrow = testOnBorrow;
        afterPropertiesSet();
    }

    /**
	 * @return
	 * @uml.property  name="testOnReturn"
	 */
    public boolean isTestOnReturn() {
        return testOnReturn;
    }

    /**
	 * @param testOnReturn
	 * @uml.property  name="testOnReturn"
	 */
    public void setTestOnReturn(boolean testOnReturn) {
        this.testOnReturn = testOnReturn;
        afterPropertiesSet();
    }

    /**
	 * @return
	 * @uml.property  name="testWhileIdle"
	 */
    public boolean isTestWhileIdle() {
        return testWhileIdle;
    }

    /**
	 * @param testWhileIdle
	 * @uml.property  name="testWhileIdle"
	 */
    public void setTestWhileIdle(boolean testWhileIdle) {
        this.testWhileIdle = testWhileIdle;
        afterPropertiesSet();
    }
}
